/**
 * TabVD is a Virtual Desktop Client/Server designed specifically for connecting
 * from Tablet like devices.
 *
 * Copyright (C) 2011 Subhabrata Ghosh
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * AbstractConfiguration.cpp
 *
 *  Created on: May 24, 2011
 *      Author: subhagho
 */

#include <iostream>
#include <fstream>

#include "AbstractConfiguration.h"

namespace tabvd {

    AbstractConfiguration::AbstractConfiguration() {
        state = STATE_NONE;
        config = new Config();
    }

    AbstractConfiguration::~AbstractConfiguration() {
        if (config != NULL) {
            delete config;
        }
    }

    EInstanceState AbstractConfiguration::getState() {
        return state;
    }

    bool AbstractConfiguration::containsKey(const std::string key) {
        if (config) {
            if (config->count(key) > 0)
                return true;
        }
        return false;
    }

    const std::string AbstractConfiguration::get(const std::string key) {
        if (config) {
            if (containsKey(key)){
                ConfigValue value = config->at(key);
                return value.getvalue();
            }
        }
        return NULLSTR;
    }

    int AbstractConfiguration::getInt(const std::string key) {
        if (config) {
            if (containsKey(key)){
                return atoi(get(key).c_str());
            }
        }
        return (int)NULLINT;
    }

    long AbstractConfiguration::getLong(const std::string key) {
        if (config) {
            if (containsKey(key)){
                return atol(get(key).c_str());
            }
        }
        return NULLINT;
    }

    double AbstractConfiguration::getDouble(const std::string key) {
        if (config) {
            if (containsKey(key)){
                return atof(get(key).c_str());
            }
        }
        return NULLINT;
    }

    DateTime* AbstractConfiguration::getDatetime(const std::string key) {
        if (config) {
            if (containsKey(key)){
                return getDatetime(key, _DEFAULT_DATE_FORMAT_);
            }
        }
        return NULL;
    }

    DateTime* AbstractConfiguration::getDatetime(const std::string key,
            const std::string format) {
        if (config) {
            if (containsKey(key)){
                std::string value = get(key);
                if (value.size() > 0) {
                    struct tm* tmptr = (tm*) malloc(sizeof(tm));
                    if (tmptr) {
                        if (strptime(value.c_str(), format.c_str(), tmptr)) {
                            time_t ltime = mktime(tmptr);
                            DateTime* dt = new DateTime((long) ltime);
                            if (dt) {
                                return dt;
                            }
                            free(tmptr);
                        }
                    }
                }
            }
        }
        return NULL;
    }

    bool AbstractConfiguration::set(const std::string key,
            const std::string value) {
        if (config) {
            if (containsKey(key)){
                ConfigValue cv = config->at(key);
                cv.setvalue(value);
                return true;
            }
            if (cdefs) {
                try {
                    ConfigElement ce = cdefs->at(key);
                    ConfigValue cv(ce, value);
                    return config->insert(Config::value_type(key, cv)).second;
                } catch (...) {
                    return false;
                }
            }
        }
        return false;
    }

    bool AbstractConfiguration::remove(const std::string key) {
        if (config) {
            if (config->erase(key) > 0)
                return true;
        }
        return false;
    }

    bool AbstractConfiguration::loaddefs(const std::string& deffile){
        cdefs = new ConfigDef();
        if (!cdefs)
            return false;

        int buffsz = 1024;
        char buffer[buffsz];

        std::ifstream stream(deffile.c_str(), std::fstream::in);
        while (true) {
            stream.getline(buffer, buffsz);

            if (stream.eof()) {
                if (stream.gcount() > 0) {
                    ConfigElement* ce = parsedef(buffer);
                    if (ce) {
                        cdefs->insert(ConfigDef::value_type(*(ce->getkey()), *ce));    
                        delete ce;
                    }  
                }
                break;
            }
            if (!stream.good()) {
                throw Exception("Error during reading file [" + deffile + "]");
            }
            ConfigElement* ce = parsedef(buffer);
            if (ce) {
                cdefs->insert(ConfigDef::value_type(*(ce->getkey()), *ce));    
                delete ce;
            }  
        }

        stream.close();

        if (cdefs->size() <= 0)
            return false;

        return true;
    }

    ConfigElement* AbstractConfiguration::parsedef(char* buffer) {
        std::string line(buffer);
        size_t cmnt = line.find("#");
        if (cmnt != std::string::npos){
            line = line.substr(0, cmnt);
        }

        size_t si = std::string::npos;
        size_t ei = std::string::npos;
        size_t ln = 0;
        
        std::string key;
        std::string nam;
        std::string smt;
        std::string svi;
        bool mdt = false;
        bool vis = false;

        si = line.find(':', 0);
        if (si != std::string::npos) {
            key = line.substr(0, si);
            ei = line.find(':', si + 1);
            if (ei != std::string::npos) {
                ln = ei - si - 1;
                nam = line.substr(si + 1, ln);
                si = line.find(':', ei + 1);
                if (si != std::string::npos){
                    ln = si - ei - 1;
                    smt = line.substr(ei + 1, ln);
                    svi = line.substr(si + 1);
                }
                else {
                   smt = line.substr(ei + 1); 
                }
                if (smt.length() > 0){
                    if (smt.compare("true") == 0)
                        mdt = true;
                }
                if (svi.length() > 0) {
                    if (svi.compare("true") == 0)
                        vis = true;
                }
            }
            else {
                nam = line.substr(si + 1);
            }
            if (key.length() > 0 && nam.length() > 0){
                ConfigElement* ce = new ConfigElement(key, nam, mdt, vis);
                printf("Config Element [k = %s, n = %s, m = %d, v = %d]\n", ce->getkey()->c_str(), ce->getname()->c_str(), ce->ismandatory(), ce->isvisible());
                return ce;
            }
        }
        return NULL;
    }
}
